#ifndef __DALELIB_SOCKET_H__
#define __DALELIB_SOCKET_H__

#include <dalelib/maths.h>

#include <Wincon.h>
#include <winsock2.h>
#include <fstream>
#include <map>
#include <vector>
#include <string>
using namespace std;



namespace dalelib
{

class Socket
{
protected:

	SOCKET _socket;
	int _port_number;

public:

	Socket()
		: _socket(INVALID_SOCKET)
	{ }

	~Socket()
	{
		shutdown(_socket,SD_SEND); // Shutdown socket
		closesocket(_socket); // Close socket entirely
		WSACleanup(); // Cleanup Winsock
	}

	virtual void init(const int _PortNumber) = 0;

	int sendMessage(string _Message)
	{
//		if(_connected_to_server)
		{
			return send(_socket,_Message.c_str(),_Message.length(),0);
		}
		return 0;
	}
};

//*********************************************************************************************

class SocketServer
	: public Socket
{
private:


public:

	SocketServer()
		: Socket()
	{ }

	void init(const int _PortNumber)
	{
		_port_number = _PortNumber;

		WSADATA WsaDat;
		if( WSAStartup(MAKEWORD(2,2),&WsaDat)!=0 )
		{
			std::cout<<"WSA Initialization failed!\n";
			WSACleanup();
			return;
		}

	//	mysocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
		_socket = socket(AF_UNSPEC,SOCK_STREAM,IPPROTO_TCP);
		if( _socket==INVALID_SOCKET )
		{
			std::cout<<"Socket creation failed.\n";
			WSACleanup();
			return;
		}

		SOCKADDR_IN serverInf;
		serverInf.sin_family = AF_INET;
		serverInf.sin_addr.s_addr = INADDR_ANY;
		serverInf.sin_port = htons(_port_number);

		if( bind(_socket,(SOCKADDR*)(&serverInf),sizeof(serverInf))==SOCKET_ERROR )
		{
			std::cout<<"Unable to bind socket!\n";
			WSACleanup();
			return;
		}

	}

	string listenForMessage()
	{
		listen(_socket,1);

		SOCKET TempSock = SOCKET_ERROR;
		int iterations = 0;
		const int MAX_SOCKET_WAIT_ITERATIONS = 10;
		while( TempSock==SOCKET_ERROR /* && ++iterations<MAX_SOCKET_WAIT_ITERATIONS */ )
		{
			TempSock = accept(_socket,NULL,NULL);
		}

	//	std::cout<<"Client connected!\n";

		// Display message from server
		char buffer[128];
		memset(buffer,0,127);
		int inDataLength = recv(TempSock,buffer,128,0);

		return string(buffer); // message
	}

};

//*********************************************************************************************

class SocketClient
	: public Socket
{
private:

	bool _connected_to_server;

public:

	SocketClient()
		: Socket()
		, _connected_to_server(false)
	{ }

	const bool isConnectedToServer() const { return _connected_to_server; }

	void init(const int _PortNumber)
	{
		_port_number = _PortNumber;

		// Initialise Winsock
		WSADATA WsaDat;
		if(WSAStartup(MAKEWORD(2,2),&WsaDat)!=0)
		{
			std::cout<<"Winsock error - Winsock initialization failed\r\n";
			WSACleanup();
			return;
		}

		// Create our socket
		_socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
		if( _socket==INVALID_SOCKET )
		{
			std::cout<<"Winsock error - Socket creation Failed!\r\n";
			WSACleanup();
			return;
		}

		// Resolve IP address for hostname
		struct hostent *host;
		if((host = gethostbyname("localhost"))==NULL)
		{
			std::cout<<"Failed to resolve hostname.\r\n";
			WSACleanup();
			return;
		}

		// Setup our socket address structure
		SOCKADDR_IN SockAddr;
		SockAddr.sin_port = htons(_port_number);
		SockAddr.sin_family = AF_INET;
		SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);

		// Attempt to connect to server
		if( connect(_socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr))!=0 )
		{
			std::cout<<"Failed to establish connection with server\r\n";
			WSACleanup();
			_connected_to_server = false;
			return;
		}
		else
		{
	//		std::cout<<"Established connection with server\r\n";
			_connected_to_server = true;
		}
	}

};

} // namespace dalelib

#endif
